package com.example.gfile.pool;

import com.example.gfile.entity.FileDetail;
import com.example.gfile.entity.FileHead;
import com.example.gfile.entity.FileTemplate;
import com.example.gfile.entity.Filed;
import com.example.gfile.random.IRandomData;
import com.example.gfile.util.SpringContextUtil;
import com.example.gfile.util.UniCodeSeparatorUtil;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StopWatch;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.example.gfile.entity.FileTemplate.CUSTOM_CONTENT;
import static com.example.gfile.entity.FileTemplate.CUSTOM_FILED;

/**
 * 文件生成线程
 */
public class BatchCreateFileThread implements Runnable {

    private final Logger LOG = LoggerFactory.getLogger(getClass());

    private File file;
    private FileTemplate fileTemplate;
    private Map<String, IRandomData> serviceMap;
    private CountDownLatch countDownLatch;

    public BatchCreateFileThread(File file, FileTemplate fileTemplate, CountDownLatch countDownLatch) {
        this.serviceMap = loadServiceMap();
        this.file = file;
        this.fileTemplate = fileTemplate;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        try {
            touch();
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
            FileChannel channel = randomAccessFile.getChannel();
            StopWatch watch = new StopWatch("批量文件生成");
            watch.start("文件头写入");
            writeHead(channel);
            watch.stop();
            watch.start("文件明细写入");
            writeDetail(channel);
            writeEnd(channel);
            watch.stop();
            countDownLatch.countDown();
            LOG.info("{}total:{}ms", watch.prettyPrint(), watch.getTotalTimeMillis());
        } catch (IOException e) {
            LOG.error("{}：文件生成异常", e.getMessage(), e);
        }
    }

    private void writeEnd(FileChannel channel) throws IOException {
        String endTextarea = fileTemplate.getEndTextarea();
        if (StringUtils.isEmpty(endTextarea)) {
            return;
        }
        long offset = file.length();
        byte[] bytes = endTextarea.getBytes(fileTemplate.getFileEncoding());
        MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, offset, bytes.length);
        buffer.put(bytes);
    }

    private void writeDetail(FileChannel channel) throws IOException {
        if (!fileTemplate.isContainDetailData()) {
            return;
        }

        List<FileDetail> fileDetailList = fileTemplate.getFileDetailList();
        //每行按1000长度计算
        long offset = file.length();
        //多阶段明细循环
        for (FileDetail fileDetail : fileDetailList) {
            int fileDetailDataCount = fileDetail.getFileDetailDataCount();
            //明细循环
            for (int i = 0; i < fileDetailDataCount; i++) {
                List<Filed> filedList = fileDetail.getFiledList();
                String separator = UniCodeSeparatorUtil.convert(fileTemplate.getSeparator());
                StringBuilder builder = new StringBuilder(1000);

                int finalI = i;
                List<String> FiledValueList = filedList.stream().map(filed -> {
                    filed.setLineIndex(finalI);
                    IRandomData iRandomDataImpl = serviceMap.get(filed.getType());
                    Function< Filed, String> function = iRandomDataImpl.data();
                    String apply = function.apply(filed);
                    return merge(filed, apply);
                }).collect(Collectors.toList());

                String data = separatorEnd(FiledValueList, separator, fileTemplate.isDetailSeparatorEnd());
                builder.append(data);
                builder.append("\r\n");
                byte[] bytes = builder.toString().getBytes(fileTemplate.getFileEncoding());
                MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, offset, bytes.length);
                buffer.put(bytes);
                offset = offset + bytes.length;
            }

        }
    }

    /**
     * 写入文件头部分
     */
    private void writeHead(FileChannel channel) throws IOException {
        String headData = buildHead();
        if (StringUtils.isEmpty(headData)) {
            return;
        }
        byte[] bytes = headData.getBytes(fileTemplate.getFileEncoding());
        MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, bytes.length);
        buffer.put(bytes);
    }

    private String buildHead() {
        String customizeSelect = fileTemplate.getFileHeadCustomizeSelect();
        String separator = UniCodeSeparatorUtil.convert(fileTemplate.getSeparator());
        StringBuilder builder = new StringBuilder();
        if (StringUtils.equals(customizeSelect, CUSTOM_CONTENT)) {
            builder.append(fileTemplate.getCustomizeContent().getCustomizeTextarea());
        }
        if (StringUtils.equals(customizeSelect, CUSTOM_FILED)) {
            FileHead fileHead = fileTemplate.getFileHead();
            List<Filed> filedList = fileHead.getFiledList();
            List<String> dataList = filedList.stream().map(filed -> {
                IRandomData iRandomDataImpl = serviceMap.get(filed.getType());
                Function<Filed, String> function = iRandomDataImpl.data();
                String apply = function.apply(filed);
                return merge(filed, apply);
            }).collect(Collectors.toList());
            String data = separatorEnd(dataList, separator, fileHead.isHeadSeparatorEnd());
            builder.append(data);
        }
        builder.append("\r\n");
        return builder.toString();
    }

    private String merge(Filed filed, String data) {
        data = leftPad(filed, data);
        return rightPad(filed, data);
    }

    private String separatorEnd(List<String> dataList, String separator, boolean separatorEnd) {
        if (separatorEnd) {
            return String.join(separator, dataList) + separator;
        } else {
            return String.join(separator, dataList);
        }
    }

    private String rightPad(Filed filed, String data) {
        if (fileTemplate.isRightAlign()) {
            data = StringUtils.rightPad(data, filed.getLength(), "");
        }
        return data;
    }

    private String leftPad(Filed filed, String data) {
        if (fileTemplate.isLeftAlign()) {
            data = StringUtils.leftPad(data, filed.getLength(), "");
        }
        return data;
    }

    private void touch() throws IOException {
        if (file.exists()) {
            FileUtils.delete(file);
        }
        FileUtils.touch(file);
    }

    /**
     * 在外围FileService类使用public FileService(List<IRandomData> dataList){}
     * 构造方法注入更方便，但那样类型为8的自增序列类只能是一个，无法做到线程之间的隔离。
     */
    public Map<String, IRandomData> loadServiceMap() {
        Map<String, IRandomData> beanList = SpringContextUtil.getBeanList(IRandomData.class);
        Map<String, IRandomData> serviceMap = new HashMap<>();
        for (String key : beanList.keySet()) {
            IRandomData iRandomData = beanList.get(key);
            serviceMap.put(iRandomData.type(), iRandomData);
        }
        return serviceMap;
    }
}
